package com.example.common.aop;

import com.alibaba.fastjson.JSON;
import com.example.common.annotation.LogAnno;
import com.example.common.annotation.LoginLogAnno;
import com.example.entity.Operate;
import com.example.mapper.OperLogMapper;
import com.example.util.ServletUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

@Aspect
@Component
@Slf4j
public class LogAopAspect {

    @Autowired
    private OperLogMapper operLogMapper;


    /**
     * 控制层日志AOP切入点
     */
    @Pointcut("@annotation(com.example.common.annotation.LogAnno)")
    public void operateLogPointCut() {

    }

    /**
     * 控制层日志AOP执行内容
     *
     * @param joinPoint
     * @param keys
     */
    @AfterReturning(value = "operateLogPointCut()", returning = "keys")
    public void aroundAdvice(JoinPoint joinPoint, Object keys) {

        // 1.方法执行前的处理，相当于前置通知
        // 获取方法签名
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        // 获取方法
        Method method = methodSignature.getMethod();
        // 获取方法上面的注解
        LogAnno logAnno = method.getAnnotation(LogAnno.class);
        // 获取操作描述的属性值
        String operateType = logAnno.operateType();

        Operate operateLog = new Operate();
        operateLog.setOperator(SecurityContextHolder.getContext().getAuthentication().getName());
        String className = joinPoint.getTarget().getClass().getName();
        String signatureName = methodSignature.getName();
        operateLog.setOperateMethod(className + "." + signatureName + "()");
        String requestMethod = ServletUtils.getRequest().getMethod();
        if ("PUT".equals(requestMethod) || "POST".equals(requestMethod)) {
            String params = argsArrayToString(joinPoint.getArgs());
            operateLog.setParams(StringUtils.substring(params, 0, 2000));
        } else {
            // 获取RequestAttributes
            RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
            // 从获取RequestAttributes中获取HttpServletRequest的信息
            HttpServletRequest request = (HttpServletRequest) requestAttributes
                    .resolveReference(RequestAttributes.REFERENCE_REQUEST);
            // 请求的参数
            Map<String, String> rtnMap = converMap(request.getParameterMap());
            operateLog.setParams(StringUtils.substring(JSON.toJSONString(rtnMap), 0, 2000));
        }
        // 接口返回值参数
        operateLog.setOperateResult(StringUtils.substring(JSON.toJSONString(keys),0,2000));
        operateLog.setOperateType(operateType);
        // 添加日志AOP记录
        operLogMapper.insert(operateLog);
    }

    /**
     * 登录登出日志AOP切入点
     */
    @Pointcut("@annotation(com.example.common.annotation.LoginLogAnno)")
    public void LoginLogPointCut() {
    }


    /**
     * 登录登出日志AOP方法执行后执行内容
     *
     * @param joinPoint
     */
    @After(value = "LoginLogPointCut()")
    public void afterAdvice(JoinPoint joinPoint) {
        // 获取方法签名
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        // 获取方法
        Method method = methodSignature.getMethod();
        // 获取方法上面的注解
        LoginLogAnno loginLogAnno = method.getAnnotation(LoginLogAnno.class);
        // 获取操作描述的属性值
        String operateType = loginLogAnno.operateType();

        Operate operateLog = new Operate();
        operateLog.setOperateType(operateType);
        String className = joinPoint.getTarget().getClass().getName();
        String signatureName = methodSignature.getName();
        operateLog.setOperateMethod(className + "." + signatureName + "()");
        // String params = argsArrayToString(joinPoint.getArgs());
        // operateLog.setParams(StringUtils.substring(params, 0, 2000));
        operateLog.setOperator(SecurityContextHolder.getContext().getAuthentication().getName());
        operLogMapper.insert(operateLog);
    }

    /**
     * 转换request 请求参数
     *
     * @param paramMap request获取的参数数组
     */
    public Map<String, String> converMap(Map<String, String[]> paramMap) {
        Map<String, String> rtnMap = new HashMap<String, String>();
        for (String key : paramMap.keySet()) {
            rtnMap.put(key, paramMap.get(key)[0]);
        }
        return rtnMap;
    }

    /**
     * 参数拼装
     */
    private String argsArrayToString(Object[] paramsArray) {
        String params = "";
        if (paramsArray != null && paramsArray.length > 0) {
            for (int i = 0; i < paramsArray.length; i++) {
                if (!isFilterObject(paramsArray[i])) {
                    Object jsonObj = JSON.toJSON(paramsArray[i]);
                    if (jsonObj != null) {
                        params += jsonObj.toString() + " ";
                    }
                }
            }
        }
        return params.trim();
    }

    /**
     * 判断是否需要过滤的对象。
     *
     * @param o 对象信息。
     * @return 如果是需要过滤的对象，则返回true；否则返回false。
     */
    public boolean isFilterObject(final Object o) {
        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse;
    }
}
